package my.edu.model.bills.purchase.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import my.edu.common.constant.MessageConstant;
import my.edu.common.constant.OrderConstant;
import my.edu.common.domain.PageDTO;
import my.edu.common.domain.OrderProductDetail;
import my.edu.common.domain.PageVO;
import my.edu.common.domain.purchase.PriceAndQuantity;
import my.edu.common.exception.BillsException;
import my.edu.common.exception.Order.AuditException;
import my.edu.common.exception.Order.DelOrderException;
import my.edu.common.exception.Order.PurchaseException;
import my.edu.common.utils.PageUtils;
import my.edu.constant.BillsType;
import my.edu.constant.DeliveryMethod;
import my.edu.constant.OrderNoConstant;
import my.edu.model.User.service.IUserService;
import my.edu.model.bills.bound.entity.OtherInboundOrderDetail;
import my.edu.model.bills.bound.entity.vo.InboundDetailVO;
import my.edu.model.bills.bound.entity.vo.InboundVO;
import my.edu.model.bills.purchase.entity.*;
import my.edu.model.bills.purchase.entity.dto.*;
import my.edu.model.bills.purchase.entity.vo.PurchaseDetailOrderVO;
import my.edu.model.bills.purchase.entity.vo.PurchaseOrderVO;
import my.edu.model.bills.purchase.entity.vo.PurchaseReturnDetailOrderVO;
import my.edu.model.bills.purchase.entity.vo.PurchaseReturnOrderVO;
import my.edu.model.bills.purchase.mapper.PurchaseReturnOrderMapper;
import my.edu.model.commodity.service.IProductService;
import my.edu.model.information.service.IAccountService;
import my.edu.model.information.service.ISupplierService;
import my.edu.security.userEntity.UserDetail;
import my.edu.utils.ChildrenLoader;
import my.edu.utils.OrderNoUtil;
import my.edu.utils.OrderServiceUtil;
import my.edu.websocket.BillWebSocket;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class PurchaseReturnOrderServiceImpl extends ServiceImpl<PurchaseReturnOrderMapper, PurchaseReturnOrder> implements IPurchaseReturnOrderService {

    private record PurchaseReturnDTOResult(List<PurchaseSalesExpensesDTO> purchaseSalesExpensesDTOList,
                                           PriceAndQuantity priceAndQuantity) {
    }

    @Autowired
    private IPurchaseReturnOrderDetailService purchaseReturnOrderDetailService;

    @Autowired
    private DeliveryMethod deliveryMethod;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IProductService productService;
    @Autowired
    private OrderNoConstant orderNoConstant;
    @Autowired
    private IPurchaseSalesExpensesService purchaseSalesExpensesService;
    @Autowired
    private IPurchaseOrderService purchaseOrderService;

    @Autowired
    private ISupplierService supplierService;
    @Autowired
    private IUserService userService;
    @Autowired
    private OrderServiceUtil orderServiceUtil;
    @Autowired
    private BillWebSocket billWebSocket;


    @Override
    @Transactional
    public void saveNew(PurchaseReturnDTO purchaseReturnDTO) {
        //校验DTO并返回订单详情和商品详情
        PurchaseReturnDTOResult result = getDTOResult(purchaseReturnDTO);
        PurchaseReturnOrder purchaseReturnOrder = setOrderField(BeanUtil.copyProperties(purchaseReturnDTO, PurchaseReturnOrder.class,
                "id", "purchaseReturnDetailDTOList", "purchaseSalesExpensesDTOList"), result, true);
        //先在上面设置完源库存，然后在新增
        save(purchaseReturnOrder);
        //如果有对应的购货单设置退款状态和退款单id
        if (purchaseReturnOrder.getPurchaseOrderId() != null && purchaseReturnOrder.getPurchaseOrderId() != 0) {
            List<OrderProductDetail> orderProductDetails = purchaseReturnDTO.getPurchaseReturnDetailDTOList()
                    .stream().map(i -> (OrderProductDetail) i).toList();
            purchaseOrderService.updatePurchaseReturnOrderId(purchaseReturnOrder, orderProductDetails);
        }
        //保存detail和采购费用
        purchaseReturnOrderDetailService.saveBatchByOrderDTO(purchaseReturnDTO, purchaseReturnOrder.getId());
        if (result.purchaseSalesExpensesDTOList != null && !result.purchaseSalesExpensesDTOList.isEmpty())
            purchaseSalesExpensesService.saveBatchByDTO(BillsType.PURCHASERETURN, result.purchaseSalesExpensesDTOList, purchaseReturnOrder.getId(), purchaseReturnOrder.getSupplierId(), purchaseReturnOrder.getSupplierName());
        billWebSocket.sendToAllClient(BillsType.PURCHASERETURN,"单据类型：采购退货单;单据编号："+purchaseReturnOrder.getOrderNo()+";提交人："+purchaseReturnOrder.getCreatedName());

    }

    @Override
    public void updateOrder(PurchaseReturnDTO purchaseReturnDTO) {
        //存在性和有无审核校验
        PurchaseReturnOrder purchaseReturnOrderByid = getById(purchaseReturnDTO.getId());
        if (purchaseReturnOrderByid == null) throw new PurchaseException(MessageConstant.ORDER_NOT_EXIST);
        if (purchaseReturnOrderByid.getIsAudited() != null && purchaseReturnOrderByid.getIsAudited() == 1)
            throw new PurchaseException(MessageConstant.ISAUDIT_CAN_NOT_UPDATE);
        //校验DTO并返回订单详情和商品详情
        PurchaseReturnDTOResult result = getDTOResult(purchaseReturnDTO);
        //设置更新字段
        PurchaseReturnOrder purchaseReturnOrder = BeanUtil.copyProperties(purchaseReturnDTO, PurchaseReturnOrder.class,
                "id", "purchaseReturnDetailDTOList", "purchaseSalesExpensesDTOList");
        //手动填入版本信息
        purchaseReturnOrder.setVersion(purchaseReturnOrderByid.getVersion());
        purchaseReturnOrder.setId(purchaseReturnOrderByid.getId());
        setOrderField(purchaseReturnOrder, result, false);
        updateById(purchaseReturnOrder);
        //检查对应购货单
        if (!Objects.equals(purchaseReturnDTO.getPurchaseOrderId(), purchaseReturnOrderByid.getPurchaseOrderId())) {
            List<OrderProductDetail> orderProductDetails = purchaseReturnDTO.getPurchaseReturnDetailDTOList()
                    .stream().map(i -> (OrderProductDetail) i).toList();
            purchaseOrderService.updatePurchaseReturnOrderId(List.of(purchaseReturnOrderByid.getId()), purchaseReturnOrder, orderProductDetails);
        }
        //保存detail和采购费用
        purchaseReturnOrderDetailService.updateBatchByOrderDTO(purchaseReturnDTO, purchaseReturnOrder.getId());
        purchaseSalesExpensesService.updateBatchByDTO(purchaseReturnOrder.getId(), BillsType.PURCHASERETURN, result.purchaseSalesExpensesDTOList(), purchaseReturnOrder.getId(), purchaseReturnOrder.getSupplierId(), purchaseReturnOrder.getSupplierName());
        billWebSocket.sendToAllClient(BillsType.PURCHASERETURN,"更新;单据类型：采购退货单;单据编号："+purchaseReturnOrder.getOrderNo()+";提交人："+purchaseReturnOrder.getUpdateName());

    }


    @Override
    @Transactional
    public void removeOrder(List<Long> ids) {
        List<PurchaseReturnOrder> purchaseReturnOrders = listByIds(ids);
        for (PurchaseReturnOrder purchaseReturnOrder : purchaseReturnOrders) {
            if (purchaseReturnOrder.getIsAudited() != null && purchaseReturnOrder.getIsAudited() == 1)
                throw new BillsException(purchaseReturnOrder.getOrderNo() + MessageConstant
                        .ISAUDIT_CAN_NOT_DEL);
        }
        removeBatchByIds(purchaseReturnOrders);
        purchaseReturnOrderDetailService.removeByOrderId(ids);
        purchaseSalesExpensesService.removeByOrderTypeAndId(BillsType.PURCHASERETURN, ids);
        //如果有对应的购货单设置退款状态和退款单id
        purchaseOrderService.removePurchaseReturnOrderId(ids);
    }


    @Override
    @Transactional
    public void auditOrder(List<Long> ids) {
        updateAuditOrder(ids, true);
    }

    private void updateAuditOrder(List<Long> ids, boolean isAudit) {
        List<PurchaseReturnOrder> purchaseReturnOrderList = listByIds(ids);
        if (purchaseReturnOrderList.isEmpty()) throw new PurchaseException(MessageConstant.ORDER_NOT_EXIST);
        //检查是否重复操作
        for (PurchaseReturnOrder purchaseReturnOrder : purchaseReturnOrderList) {
            Integer isAudited = purchaseReturnOrder.getIsAudited();
            if ((isAudited == null || isAudited == 0) && !isAudit)
                throw new AuditException(purchaseReturnOrder.getOrderNo() + MessageConstant.NOTAUDITED);
            else if (isAudited != null && isAudited == 1 && isAudit)
                throw new AuditException(purchaseReturnOrder.getOrderNo() + MessageConstant.ISAUDITED);
        }
        //更新状态
        orderServiceUtil.setAuditStatus(this, ids, isAudit);
        //计算库存
        List<Long> purchaseReturnIdlist = purchaseReturnOrderList.stream().map(PurchaseReturnOrder::getId).toList();
        List<OrderProductDetail> orderProductDetails = purchaseReturnOrderDetailService
                .list(Wrappers.<PurchaseReturnOrderDetail>lambdaQuery()
                        .in(PurchaseReturnOrderDetail::getPurchaseReturnOrderId, purchaseReturnIdlist))
                .stream().map(i -> (OrderProductDetail) i).toList();
        //计算修改的账户金额
        Map<Long, BigDecimal> accountIdToTotalAmountMap = orderServiceUtil.getAccountIdToTotalAmountMap(purchaseReturnOrderList);
        if (isAudit) {
            //更新库存
            productService.subCurrentStock(orderProductDetails);
            //更新账户金额
            accountService.addBalance(accountIdToTotalAmountMap);
        } else {
            productService.addCurrentStock(orderProductDetails);
            accountService.subBalance(accountIdToTotalAmountMap);
        }
    }

    @Override
    @Transactional
    public void rauditOrder(List<Long> ids) {
        updateAuditOrder(ids, false);
    }

    @Override
    public PageVO<PurchaseReturnOrderVO> getPurchaseOrder(PageDTO page, PurchaseReturnOrderSearchDTO purchaseReturnOrderSearchDTO) {
        MPJLambdaWrapper<PurchaseReturnOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(PurchaseReturnOrder.class, PurchaseReturnOrderVO.class)
//                .selectCollection(PurchaseSalesExpenses.class, PurchaseReturnOrderVO::getPurchaseSalesExpensesDTOList)
//                .selectCollection(PurchaseReturnOrderDetail.class, PurchaseReturnOrderVO::getPurchaseReturnDetailDTOList)
//                .leftJoin(PurchaseSalesExpenses.class, on -> on
//                        .eq(PurchaseSalesExpenses::getSourceDocumentId, PurchaseReturnOrder::getId)
//                        .eq(PurchaseSalesExpenses::getBillsType, BillsType.PURCHASERETURN))
//                .leftJoin(PurchaseReturnOrderDetail.class, on -> on
//                        .eq(PurchaseReturnOrderDetail::getPurchaseReturnOrderId, PurchaseReturnOrder::getId))
                .like(purchaseReturnOrderSearchDTO.getOrderNo() != null && !purchaseReturnOrderSearchDTO.getOrderNo().isEmpty(),
                        PurchaseReturnOrder::getOrderNo, purchaseReturnOrderSearchDTO.getOrderNo())
                .between(purchaseReturnOrderSearchDTO.getBeginTime() != null,
                        PurchaseReturnOrder::getOrderDate, purchaseReturnOrderSearchDTO.getBeginTime(), purchaseReturnOrderSearchDTO.getEndTime())
                .eq(purchaseReturnOrderSearchDTO.getDeliveryMethod() != null,
                        PurchaseReturnOrder::getDeliveryMethod, purchaseReturnOrderSearchDTO.getDeliveryMethod())
                .eq(purchaseReturnOrderSearchDTO.getReturnStatus() != null && !purchaseReturnOrderSearchDTO.getReturnStatus().isEmpty(),
                        PurchaseReturnOrder::getRefundStatus, purchaseReturnOrderSearchDTO.getReturnStatus())
                .eq(purchaseReturnOrderSearchDTO.getIsAudited() != null,
                        PurchaseReturnOrder::getIsAudited, purchaseReturnOrderSearchDTO.getIsAudited())
                .eq(purchaseReturnOrderSearchDTO.getSalesmanId() != null,
                        PurchaseReturnOrder::getSalesmanId, purchaseReturnOrderSearchDTO.getSalesmanId())
                .eq(purchaseReturnOrderSearchDTO.getPurchaseOrderId() != null,
                        PurchaseReturnOrder::getPurchaseOrderId, purchaseReturnOrderSearchDTO.getPurchaseOrderId())
                .eq(purchaseReturnOrderSearchDTO.getSettlementAccountId() != null,
                        PurchaseReturnOrder::getSettlementAccountId, purchaseReturnOrderSearchDTO.getSettlementAccountId())
                .eq(purchaseReturnOrderSearchDTO.getAuditorId() != null,
                        PurchaseReturnOrder::getAuditorId, purchaseReturnOrderSearchDTO.getAuditorId())
                .eq(purchaseReturnOrderSearchDTO.getLogisticsId() != null,
                        PurchaseReturnOrder::getLogisticsId, purchaseReturnOrderSearchDTO.getLogisticsId())
                .eq(purchaseReturnOrderSearchDTO.getSupplierId() != null,
                        PurchaseReturnOrder::getSupplierId, purchaseReturnOrderSearchDTO.getSupplierId())
                .orderByDesc(PurchaseReturnOrder::getCreateTime);
        Page<PurchaseReturnOrderVO> purchaseReturnOrderVOPage = baseMapper.selectJoinPage(new Page<>(page.getPage(), page.getSize()), PurchaseReturnOrderVO.class, wrapper);
        if (CollUtil.isNotEmpty(purchaseReturnOrderVOPage.getRecords())) {
            ChildrenLoader.loadAndFillDTO(
                    purchaseReturnOrderVOPage.getRecords(),
                    PurchaseReturnOrderVO::getId,
                    ids -> purchaseReturnOrderDetailService.list(
                            Wrappers.<PurchaseReturnOrderDetail>lambdaQuery().in(PurchaseReturnOrderDetail::getPurchaseReturnOrderId, ids)),
                    PurchaseReturnDetailOrderVO::getPurchaseReturnOrderId,
                    PurchaseReturnDetailOrderVO.class,
                    PurchaseReturnOrderVO::setPurchaseReturnDetailDTOList);
        };
        return PageUtils.toPage(purchaseReturnOrderVOPage);
    }

    @Override
    public PurchaseReturnOrderVO getOrderOne(Long id) {
        MPJLambdaWrapper<PurchaseReturnOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(PurchaseReturnOrder.class, PurchaseReturnOrderVO.class)
                .selectCollection(PurchaseReturnOrderDetail.class, PurchaseReturnOrderVO::getPurchaseReturnDetailDTOList)
                .selectCollection(PurchaseSalesExpenses.class, PurchaseReturnOrderVO::getPurchaseSalesExpensesDTOList)
                .leftJoin(PurchaseReturnOrderDetail.class, PurchaseReturnOrderDetail::getPurchaseReturnOrderId, PurchaseReturnOrder::getId)
                .leftJoin(PurchaseSalesExpenses.class, on -> on
                        .eq(PurchaseSalesExpenses::getSourceDocumentId, PurchaseReturnOrder::getId)
                        .eq(PurchaseSalesExpenses::getBillsType, BillsType.PURCHASERETURN))
                .eq(PurchaseReturnOrder::getId, id);
        return baseMapper.selectJoinOne(PurchaseReturnOrderVO.class, wrapper);
    }

    @Override
    public JSONArray getPurchaseReturnrderOnly() {
        List<PurchaseReturnOrder> list = list(Wrappers.<PurchaseReturnOrder>lambdaQuery()
                .orderByDesc(PurchaseReturnOrder::getCreateTime));
        JSONArray jsonArray = list.stream()
                .map(order -> {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("id", order.getId());
                    jsonObject.put("orderNo", order.getOrderNo());
                    return jsonObject;
                })
                .collect(Collectors.toCollection(JSONArray::new));
        return jsonArray;
    }

    @Override
    public List<PurchaseReturnOrderVO> listOnly(PurchaseReturnOrderSearchDTO purchaseReturnOrderSearchDTO) {
        List<PurchaseReturnOrderVO> list = list(Wrappers.<PurchaseReturnOrder>lambdaQuery()
                .like(purchaseReturnOrderSearchDTO.getOrderNo() != null && !purchaseReturnOrderSearchDTO.getOrderNo().isEmpty(),
                        PurchaseReturnOrder::getOrderNo, purchaseReturnOrderSearchDTO.getOrderNo())
                .between(purchaseReturnOrderSearchDTO.getBeginTime() != null,
                        PurchaseReturnOrder::getOrderDate, purchaseReturnOrderSearchDTO.getBeginTime(), purchaseReturnOrderSearchDTO.getEndTime())
                .eq(purchaseReturnOrderSearchDTO.getReturnStatus() != null && !purchaseReturnOrderSearchDTO.getReturnStatus().isEmpty(),
                        PurchaseReturnOrder::getRefundStatus, purchaseReturnOrderSearchDTO.getReturnStatus())
                .eq(purchaseReturnOrderSearchDTO.getSalesmanId() != null,
                        PurchaseReturnOrder::getSalesmanId, purchaseReturnOrderSearchDTO.getSalesmanId())
                .eq(purchaseReturnOrderSearchDTO.getAuditorId() != null,
                        PurchaseReturnOrder::getAuditorId, purchaseReturnOrderSearchDTO.getAuditorId())
                .eq(purchaseReturnOrderSearchDTO.getSupplierId() != null,
                        PurchaseReturnOrder::getSupplierId, purchaseReturnOrderSearchDTO.getSupplierId())
                .orderByDesc(PurchaseReturnOrder::getCreateTime)).stream().map(i -> BeanUtil.copyProperties(i, PurchaseReturnOrderVO.class)).toList();
        return list;
    }

    private PurchaseReturnOrder setOrderField(PurchaseReturnOrder purchaseReturnOrder, PurchaseReturnDTOResult result, boolean isNew) {
        purchaseReturnOrder.setQuantity(result.priceAndQuantity.getQuantity());//总数量
        purchaseReturnOrder.setRefundAmount(result.priceAndQuantity.getAmount());//总应退数量
        if (purchaseReturnOrder.getOrderNo() == null || purchaseReturnOrder.getOrderNo().isEmpty()) {
            //设置No
            String orderNo = OrderNoUtil.generateOrderNo(orderNoConstant.getPurchaseNo());
            purchaseReturnOrder.setOrderNo(orderNo);
        } else {
            //检查是否有重复no
            long count = count(Wrappers.<PurchaseReturnOrder>lambdaQuery().eq(PurchaseReturnOrder::getOrderNo, purchaseReturnOrder.getOrderNo()));
            if (isNew && count > 0) throw new BillsException(MessageConstant.ORDER_NO_EXISTS);
        }
        //检查优惠金额
        BigDecimal amount = purchaseReturnOrder.getRefundAmount().subtract(purchaseReturnOrder.getDiscountAmount());
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new BillsException(MessageConstant.AMOUNT_ERROR);
        }
        //应退款金额<抵扣金额加已付款金额
        if (purchaseReturnOrder.getRefundAmount().compareTo(
                purchaseReturnOrder.getRefundedAmount()
                //.add(purchaseReturnOrder.getRefundedAmountWithPayableBalance())
        )
                < 0) throw new BillsException(MessageConstant.AMOUNT_ERROR);
        purchaseReturnOrder.setAmountAfterDiscount(amount);//优惠后金额
        //设置付款状态
        setPaidStatus(purchaseReturnOrder);
        purchaseReturnOrder.setIsAudited(0);
        return purchaseReturnOrder;
    }

    private PurchaseReturnDTOResult getDTOResult(PurchaseReturnDTO purchaseReturnDTO) {
        List<PurchaseSalesExpensesDTO> purchaseSalesExpensesDTOList = purchaseReturnDTO.getPurchaseSalesExpensesDTOList();
        List<PurchaseReturnDetailOrderDTO> purchaseReturnDetailDTOList = purchaseReturnDTO.getPurchaseReturnDetailDTOList();
        //购货单存在校验
        purchaseOrderService.checkPurchaseOrder(purchaseReturnDTO);

        supplierService.checkSupplier(purchaseReturnDTO.getSupplierId());
        userService.checkUser(purchaseReturnDTO.getSalesmanId());

        //检查交货方式
        deliveryMethod.checkDeliveryMethod(purchaseReturnDTO.getDeliveryMethod());
        //检查结算账户
        accountService.checkAccount(purchaseReturnDTO.getSettlementAccountId());
        //校验detail，并返回商品总价和数量
        PriceAndQuantity priceAndQuantity = orderServiceUtil.checkAndGetProductDetail(OrderConstant.IGNORE_PRICE, purchaseReturnDetailDTOList.stream().map(i -> (OrderProductDetail) i).toList());
        PurchaseReturnDTOResult result = new PurchaseReturnDTOResult(purchaseSalesExpensesDTOList, priceAndQuantity);
        return result;
    }


    private void setPaidStatus(PurchaseReturnOrder purchaseReturnOrderOrder) {
        BigDecimal amountAfterDiscount = purchaseReturnOrderOrder.getAmountAfterDiscount();
        //检查退款金额是否为0
        if (!(amountAfterDiscount == null) && amountAfterDiscount.compareTo(BigDecimal.ZERO) != 0) {
            BigDecimal refundedAmount = purchaseReturnOrderOrder.getRefundedAmount();
            if (refundedAmount.compareTo(BigDecimal.ZERO) == 0)
                //没有付一分钱
                purchaseReturnOrderOrder.setRefundStatus(OrderConstant.NOT_REPAYMENT);
            else if (amountAfterDiscount.compareTo(refundedAmount) == 0)
                //付钱的数量等于优惠后的数量完成退款
                purchaseReturnOrderOrder.setRefundStatus(OrderConstant.GET_REPAYMENT);
                //部分退款
            else if (amountAfterDiscount.compareTo(refundedAmount) > 0)
                purchaseReturnOrderOrder.setRefundStatus(OrderConstant.PART_REPAYMENT);
            else throw new PurchaseException(MessageConstant.AMOUNT_ERROR);
        } else purchaseReturnOrderOrder.setRefundStatus(OrderConstant.GET_REPAYMENT);
    }
}